import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { afterEach, beforeEach, describe, expect, it } from "vitest";

import { RequestClient } from "./request-client";

describe("requestClient", () => {
	let mock: MockAdapter;
	let requestClient: RequestClient;

	beforeEach(() => {
		mock = new MockAdapter(axios);
		requestClient = new RequestClient();
	});

	afterEach(() => {
		mock.reset();
	});

	it("should successfully make a GET request", async () => {
		mock.onGet("test/url").reply(200, { data: "response" });

		const response = await requestClient.get("test/url");

		expect(response.data).toEqual({ data: "response" });
	});

	it("should successfully make a POST request", async () => {
		const postData = { key: "value" };
		const mockData = { data: "response" };
		mock.onPost("/test/post", postData).reply(200, mockData);
		const response = await requestClient.post("/test/post", postData);
		expect(response.data).toEqual(mockData);
	});

	it("should successfully make a PUT request", async () => {
		const putData = { key: "updatedValue" };
		const mockData = { data: "updated response" };
		mock.onPut("/test/put", putData).reply(200, mockData);
		const response = await requestClient.put("/test/put", putData);
		expect(response.data).toEqual(mockData);
	});

	it("should successfully make a DELETE request", async () => {
		const mockData = { data: "delete response" };
		mock.onDelete("/test/delete").reply(200, mockData);
		const response = await requestClient.delete("/test/delete");
		expect(response.data).toEqual(mockData);
	});

	it("should handle network errors", async () => {
		mock.onGet("/test/error").networkError();
		try {
			await requestClient.get("/test/error");
			expect(true).toBe(false);
		} catch (error: any) {
			expect(error.isAxiosError).toBe(true);
			expect(error.message).toBe("Network Error");
		}
	});

	it("should handle timeout", async () => {
		mock.onGet("/test/timeout").timeout();
		try {
			await requestClient.get("/test/timeout");
			expect(true).toBe(false);
		} catch (error: any) {
			expect(error.isAxiosError).toBe(true);
			expect(error.code).toBe("ECONNABORTED");
		}
	});

	it("should successfully upload a file", async () => {
		const fileData = new Blob(["file contents"], { type: "text/plain" });

		mock.onPost("/test/upload").reply((config) => {
			return config.data instanceof FormData && config.data.has("file")
				? [200, { data: "file uploaded" }]
				: [400, { error: "Bad Request" }];
		});

		const response = await requestClient.upload("/test/upload", {
			file: fileData,
		});
		expect(response.data).toEqual({ data: "file uploaded" });
	});

	it("should successfully download a file as a blob", async () => {
		const mockFileContent = new Blob(["mock file content"], {
			type: "text/plain",
		});

		mock.onGet("/test/download").reply(200, mockFileContent);

		const res = await requestClient.download("/test/download");

		expect(res.data).toBeInstanceOf(Blob);
	});
});
